package jaux.tank.api.controller;

import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.HttpClientErrorException;

import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import jaux.tank.admin.config.properties.TankProperties;
import jaux.tank.bean.constant.state.ManagerStatus;
import jaux.tank.bean.core.ShiroUser;
import jaux.tank.bean.dto.UserDto;
import jaux.tank.bean.entity.system.User;
import jaux.tank.bean.enumeration.BizExceptionEnum;
import jaux.tank.bean.exception.TankException;
import jaux.tank.bean.vo.LoginResult;
import jaux.tank.bean.vo.Token;
import jaux.tank.bean.vo.UserInfo;
import jaux.tank.bean.vo.UserSession;
import jaux.tank.bean.vo.UserSettings;
import jaux.tank.bean.vo.front.Ret;
import jaux.tank.bean.vo.front.Rets;
import jaux.tank.bean.vo.node.MenuNode;
import jaux.tank.dao.cache.TokenCache;
import jaux.tank.dao.system.UserRepository;
import jaux.tank.factory.UserFactory;
import jaux.tank.service.system.AccountService;
import jaux.tank.service.system.MenuService;
import jaux.tank.shiro.ShiroKit;
import jaux.tank.utils.MD5;
import jaux.tank.utils.PasswordStrengthUtil;
import jaux.tank.utils.StringUtils;
import jaux.tank.utils.ToolUtil;

/**
 * AccountController
 *
 * @author zt
 * @version 2018/9/12 0012
 */
@RestController("accountApiController")
@RequestMapping("/api/account")
public class AccountController extends BaseController{
	
	private static final String USERNAME_OR_PASSWORD_FAILURE = "用户名或密码错误";
	
	
     private Logger logger = LoggerFactory.getLogger(AccountController.class);
    @Autowired
    UserRepository userRepository;
    @Autowired
    private AccountService accountService;
    @Autowired
    private MenuService menuService;
    @Autowired
    private TokenCache tokenCache;
    
    @Autowired
    private DefaultWebSessionManager sessionManager;
    @Autowired
    TankProperties tankProps;
    
    /**
     * 用户登录<br>
     * 1，验证没有注册<br>
     * 2，验证密码错误<br>
     * 3，登录成功
     * @param userName
     * @param password
     * @return
     */
    @ApiOperation(value = "用户登录",notes = "当启用密码加密时，使用loginId的md5值作为key,使用AES-GCM算法加密，并以base64格式传输密码。")
    @PostMapping(value = "/login")
    public Ret<LoginResult> login(@RequestParam("username") String userName,
                        @RequestParam("password") String password,
                        @ApiParam("登录会话id")
                        @RequestHeader(name = "loginId", required = false) String loginId){
    	// 是否开启本地登录
    	if(!tankProps.isLocalLogin()) {
    		throw new HttpClientErrorException(HttpStatus.NOT_FOUND);
    	}
    	password = accountService.validLoginIdAndPassword(loginId, password);
    	 try {
             //1,
             User user = userRepository.findByAccount(userName);
             if (user == null) {
                 return Rets.failure(USERNAME_OR_PASSWORD_FAILURE);
             }
             String password2 = MD5.md5(password, user.getSalt());
             //2,
             if (!user.getPassword().equals(password2)) {
                 return Rets.failure(USERNAME_OR_PASSWORD_FAILURE);
             }

             String token = accountService.login(user.getId());
             logger.info("token:{}",token);
             ShiroUser shiroUser = tokenCache.getUser(token);
             LoginResult loginResult = new LoginResult();
			 loginResult.setToken(token);
			 loginResult.setShiroUserId(user.getId());
			 loginResult.setAvatar(user.getAvatar());
			 loginResult.setUsername(user.getAccount());
			 loginResult.setNickname(user.getName());
			 loginResult.setRoles(shiroUser.getRoleCodes());
			 loginResult.setAccessToken(token);
			 loginResult.setRefreshToken(token);
			 loginResult.setExpires(tokenCache.getTokenExpire(token));
			 return Rets.success(loginResult);
         } catch (Exception e) {
             logger.error(e.getMessage(), e);
         }
         return Rets.failure("登录时失败");
    }

    /**
     * 退出登录
     * @param request
     * @return
     */
    @ApiOperation(value = "退出登录")
    @PostMapping("/logout")
    public Ret<Map<String,String>> logout(HttpServletRequest request){
        String token = ShiroKit.getRequestToken();
        accountService.logout(token);
        return Rets.success();
    }

    @ApiOperation(value = "用户信息")
    @GetMapping("/info")
    public Ret<UserInfo> info(HttpServletRequest request){
        String idUser = getUserId();
        if(idUser!=null){
            Optional<User> userOpt = userRepository.findById(idUser);
            if(!userOpt.isPresent()) {
            	return Rets.failure("获取用户信息失败");
            }
			User user =  userOpt.get();
            if(StringUtils.isEmpty(user.getRoleid())){
                return Rets.failure("该用户未配置权限");
            }
            String token = ShiroKit.getRequestToken();
            ShiroUser shiroUser = tokenCache.getUser(token);

            UserInfo userInfo = UserFactory.getUserInfo(user);
			userInfo.setRoles(shiroUser.getRoleCodes());
			List<MenuNode> menuNodes =  menuService.getMenusTreeByRoleIds(shiroUser.getRoleList());
			userInfo.setMenuNodes(menuNodes);
            return Rets.success(userInfo);
        }
        return Rets.failure("获取用户信息失败");
    }
    @GetMapping("/sessions")
    @ApiOperation("用户会话")
    public Ret<Collection<UserSession>> sessions(){
		return Rets.success(sessionManager.getSessionDAO().getActiveSessions().stream()
				.filter(ss->ss.getAttributeKeys().contains("shiroUser"))
				.map(ss->{
					UserSession userSession = new UserSession();
					userSession.setHost(ss.getHost());
					userSession.setId(ss.getId());
					userSession.setLastAccessTime(ss.getLastAccessTime());
					userSession.setStartTimestamp(ss.getStartTimestamp());
					userSession.setTimeout(ss.getTimeout());
					userSession.setStopTimestamp(new Date(ss.getLastAccessTime().getTime()+ss.getTimeout()));
					userSession.setAttributes(ss.getAttributeKeys().stream().collect(Collectors.toMap(key->key.toString(), key->ss.getAttribute(key))));
					return userSession;
		}).collect(Collectors.toList()));
    }
    
    @PostMapping("/refreshToken")
	@ApiOperation("刷新token")
    public Ret<Token> refreshToken(String refreshToken){
       String userId = tokenCache.get(refreshToken);
       if(userId==null) {
    	   return Rets.failure("刷新token失败");
       }
	   String newToken = accountService.login(userId);
	   Token token = new Token();
	   token.setShiroUserId(userId);
	   token.setAccessToken(newToken);
	   token.setRefreshToken(refreshToken);
	   token.setExpires(tokenCache.getTokenExpire(newToken));
	   return Rets.success(token);
    }
    
    @RequestMapping(value = "/update", method = { RequestMethod.POST, RequestMethod.PUT})
    @ApiOperation(value = "编辑账户设置")
    public Ret<Object> update(@RequestBody UserSettings settings){
    	 String userId = getUserId();
         if(userId==null) {
      	   return Rets.failure("update account settings failed");
         }
         accountService.update(userId,settings);
            
        return Rets.success();
    }

    
    @GetMapping("/loginId")
    @ApiOperation("获取登录id,有效期1分钟")
    public Ret<String> loginId(){
    	return Rets.success(accountService.getLoginId());
    }
    /**
     * 修改当前用户的密码
     */
    @PutMapping("/changePwd")
    @ApiOperation("修改当前用户的密码")
    public Ret<String> changePwd( String oldPassword
    		, String newPassword
    		,  String newPasswordAgain) {
        if (!newPassword.equals(newPasswordAgain)) {
            return Rets.failure(BizExceptionEnum.TWO_PWD_NOT_MATCH.getMessage());
        }
        Optional<ShiroUser> shiroUser = ShiroKit.getApiRequestUser();
        if(!shiroUser.isPresent()) {
        	return Rets.failure(BizExceptionEnum.USER_NOT_EXISTED.getMessage());
        }
		String userId = shiroUser.get().getId();
        User user = userRepository.findById(userId).orElse(null);
        if(user == null) {
        	return Rets.failure(BizExceptionEnum.USER_NOT_EXISTED.getMessage());
        }
        String oldMd5 = MD5.md5(oldPassword, user.getSalt());
        if (!user.getPassword().equals(oldMd5)) {
        	return Rets.failure(BizExceptionEnum.OLD_PWD_NOT_RIGHT.getMessage());
        }
        // 验证密码强度	
        if(PasswordStrengthUtil.getPasswordStrength(newPassword)<PasswordStrengthUtil.LEVEL_STRONG) {
        	return Rets.failure(BizExceptionEnum.PWD_INVALID.getMessage());
        }
        String newMd5 = MD5.md5(newPassword, user.getSalt());
        user.setPassword(newMd5);
        userRepository.save(user);
        return Rets.success();
    }
}
